-- SkyRelicM
-- created by panyl
-- 天空之城遗迹功能模块

module("SkyRelicM", package.seeall);

local GATHER_UNIT_TIME   = 180;       -- 已经采集单位时间
local REFRESH_RELIC_TIME = 28800;     -- 刷新列表时间
local SECONDS_PER_DAY    = 86400;     -- 一天的秒数
local robRelicExtraBonus = {};
local relicBoss = {};
local relicEggs = {};
local lastRevengeIndex;               -- 上次打开复仇界面显示的INDEX
local lastRefreshInfoTime = 0;        -- 上次刷新掠夺列表时间
local whenLoginOk;
local relicPetShowList = {};          -- 同一次APP启动只抽取一次宠物显示数据
local revengePetShowList = {};

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    -- 载入配置表
    local extraBonusCSV = table.deepcopy(get_rob_relic_extra_bonus_CSV());
    for _, info in pairs(extraBonusCSV) do
        robRelicExtraBonus[info.id] = info.bonus;
    end

    local relicBossCSV = table.deepcopy(get_relic_boss_CSV());
    for _, info in pairs(relicBossCSV) do
        relicBoss[info.id] = info;
    end

    local relicEggsCSV = table.deepcopy(get_relic_eggs_CSV());
    for _, info in pairs(relicEggsCSV) do
        relicEggs[info.id] = info;
    end
end

-- 模块初始化操作
function init()
    EventMgr.removeAll("SkyRelicM");

    -- 注册玩家登录的回调
    EventMgr.register("SkyRelicM", event.LOGIN_OK, whenLoginOk);

    loadCsv();
    lastRefreshInfoTime = 0;
    relicPetShowList = {};
    revengePetShowList = {};
end

-- 获取上次打开界面时的index
function getLastRevengeIndex()
    return lastRevengeIndex;
end

-- 设置上次打开界面时的index()
function setLastRevengeIndex(index)
    lastRevengeIndex = index;
end

-- 遗迹是否开启
function isRelicFound()
    -- 功能是否开启
    if ME.user.dbase:query("features", "machine", false)
        and ME.user.dbase:query("fix_relic_already") == 1 then
        return true;
    end
    return false;
end

-- 查询额外奖励
function getRobRelicExtraBonus(winTimes)
    if winTimes then
        return robRelicExtraBonus[winTimes];
    else
        return robRelicExtraBonus;
    end
end

-- 获取当前遗迹信息
function getRelicInfo()
    return ME.user.dbase:query("relic_info", {});
end

-- 是否收集中
function isGathering()
    local info = getRelicInfo();

    if not info.scrap then
        return false;
    end

    return true;
end

-- 开始采集遗迹碎片
-- 遗迹是自动开始采集的
function startGather()
    -- 如果已经有数据了
    if isGathering() then
        return false;
    end

    return true;
end

-- 能否收集
function canCollect()
    local relicInfo = getMyRelicInfo();
    local resource = relicInfo.resource;
    if resource["scrap"] <= 0 and resource["money"] <= 0 then
        return false;
    end

    local storeSize = ME.user:queryAttrib("relic_store");
    if storeSize <= 0 then
        return false;
    end

    -- 认为超过1%才可以收集
    local percentNum = math.ceil(resource["scrap"] * 100 / storeSize);
    if percentNum <= 0 then
        return false;
    end

    return true;
end

-- 收集碎片
function collectRelic()
    -- 如果不是采集中
    if not canCollect() then
        return false;
    end
    Operation.cmd_collect_relic();
    return true;
end

-- 计算当前遗迹碎片总量
function calcTotalResource(data)
    local scrap       = data["scrap"] or 0;
    local money       = data["money"] or 0;
    local scrapSpeed  = data["scrap_speed"] or 0;
    local moneySpeed  = data["money_speed"] or 0;
    local gatherTime  = data["gather_time"] or 0;
    local curTime     = TimeM.getServerTime();
    local storeSize   = data["store_size"];
    local times;

    if gatherTime > 0 and curTime > gatherTime then
        times = math.modf((curTime - gatherTime) / GATHER_UNIT_TIME);

        if storeSize < scrap + times * scrapSpeed then
            money = math.modf((storeSize - scrap) * moneySpeed / scrapSpeed) + money;
            scrap = storeSize;
        else
            scrap = scrap + times * scrapSpeed;
            money = money + times * moneySpeed;
        end
    end

    return {
        ["scrap"] = scrap,
        ["money"] = money,
    };
end

-- 默认获取采集单位时间
function getGatherUnitTime()
    return GATHER_UNIT_TIME;
end

-- 获取掠夺详细信息
function getTargetRobInfo(target)
    local m = ME.user.dbase:query("relic_rob_list", {});
    if not m or not m[target]  then
        return {};
    end

    local info = m[target];
    local total = calcTotalResource(info);
    local exResource = FormulaM.invoke("CALC_RELIC_BONUS_AMOUNT", info);

    -- 计算当前奖励
    local bonusList = calcRobBonusList(info);

    -- 额外信息
    local extraInfo = {
            ["type"]        = "relic",
            ["bonus"]       = bonusList,
            ["last_fail"]   = info["last_rob_time"] or 0,
        }

    return table.add(info, extraInfo);
end

-- 计算掠夺奖励
function calcRobBonusList(info)
    local bonusList = {};

    local total = calcTotalResource(info);

    -- 掠夺许可证
    local robStore = FormulaM.invoke("CALC_ROB_STORE_SIZE", ME.user);
    -- 掠夺保底加成
    local robMin = FormulaM.invoke("CALC_ROB_MIN_SIZE", robStore, info["fight_ability"]);
    local scrap = total.scrap + robMin;
    -- 掠夺上限
    scrap = math.min(scrap, robStore);

    local bonusList = FormulaM.invoke("CALC_RELIC_ROB_BONUS", scrap, total.money or 0, true);

    -- 计算额外掠夺
    local exResource = FormulaM.invoke("CALC_RELIC_BONUS_AMOUNT", info);
    if exResource > 0 then
        bonusList = table.append(bonusList, {{1, 10000, exResource}});
    end

    -- 计算额外奖励
    local winTimes = ME.user.dbase:query("relic_rob_win_times") or 0;
    local extraBonus = SkyRelicM.getRobRelicExtraBonus(winTimes + 1);
    if extraBonus then
        bonusList = table.append(bonusList, extraBonus);
    end

    return bonusList;
end

-- 当前掠夺列表
function getRobList()
    local m = ME.user.dbase:query("relic_rob_list", {});

    local ret = {};
    for rid, _ in pairs(m) do
        table.insert(ret, getTargetRobInfo(rid));
    end
    return ret;
end

-- 获取复仇玩家信息
function getRevengeInfo(data)
    -- 攻击方的信息
    local info = data["attacker"];

    -- 额外信息
    info["scrap"]       = data["real_rob"] or data["scrap"];         -- 被抢资源
    info["money"]       = data["money"];
    info["result"]      = data["result"];        -- 结果
    info["record_rid"]  = data["rid"];           -- 记录的RID（不用rid是为了避免和玩家rid冲突）
    info["fail_times"]  = data["fail_times"];    -- 失败次数
    info["valid_time"]  = data["valid_time"];    -- 有效时间
    info["ex_resource"] = data["ex_resource"];   -- 额外掠夺（碎片）
    info["strike_time"] = data["strike_time"];   -- 被抢时间
    info["revenge_time"] = data["revenge_time"]; -- 复仇结果
    info["lastRevengeTime"] = data["lastRevengeTime"] or data["last_revenge_time"] or 0;

    return info;
end

-- 掠夺
function robRelic(target, force, stoneId)
    local m = ME.user.dbase:query("relic_rob_list", {});
    if not m[target] then
        trace("SkyRelicM", "玩家没有该掠夺目标：%s。\n", target);
        return false;
    end

    if m[target]["result"] == 1 then
        trace("SkyRelicM", "该目标已经胜利：%s。\n", target);
        return false;
    end

    local funcId = SkyRelicFunctionM.getIdsByRule("permit");
    if SkyRelicFunctionM.getLevel(funcId) < 1 then
        trace("SkyRelicM", "玩家没有掠夺许可证。\n");
        return false;
    end

    if isInCoolTime(m[target], "relic") and force ~= 1 then
        trace("SkyRelicM", "玩家处于掠夺冷却时间。\n");
        return false;
    end

    -- 发起掠夺
    Operation.cmd_relic_rob(target, force, stoneId);
    return true;
end

-- 当前遗迹信息
function getMyRelicInfo(attrib)
    local data = ME.user.dbase:query("relic_info", {});

    if not data["gather_time"] or data["gather_time"] <= 0 then
        -- 没有相关采集信息
        return { ["resource"]   = { ["scrap"] = 0, ["money"] = 0 },
                 ["protect"]    = data["protect"],
                 ["total_time"] = 0,};
    end

    local totalTime  = TimeM.getServerTime() - data["gather_time"];
    local scrapSpeed = ME.user:queryAttrib("scrap_speed");
    local moneySpeed = ME.user:queryAttrib("money_speed");
    local storeSize  = ME.user:queryAttrib("relic_store");

    local resource = calcTotalResource(table.add(data, {["scrap_speed"] = scrapSpeed,
        ["money_speed"] = moneySpeed, ["store_size"] = storeSize, }));

    local ret = { ["resource"] = resource, ["total_time"] = totalTime, ["protect"] = data["protect"]};

    if attrib and ret[attrib] then
        return ret[attrib];
    end

    return ret;
end

-- 遗迹采集结果
function notifyCollectResult(resource)
    local arr = resource.bonus or FormulaM.invoke("CALC_RELIC_ROB_BONUS", resource["scrap"] or 0, resource["money"] or 0);

    EventMgr.fire(event.RELIC_COLLECT_RESOURCE, {["bonus"] = arr});
end

-- 复仇
function revenge(target, force, stoneId)
    local record = ME.user.dbase:query("relic_combat_record", {})[target];

    if not record then
        trace("SkyRelicM", "玩家没有该复仇记录：%s", target);
        return false;
    end

    if isInCoolTime(record, "relic") and force ~= 1 then
        trace("SkyRelicM", "玩家处于复仇冷却时间。\n");
        return false;
    end

    -- 如果是防守成功的记录
    if record["result"] ~= 1 then
        trace("SkyRelicM", "该记录不可复仇。\n");
        return false;
    end

    -- 如果已经复仇过了
    if record["revenge_time"] then
        trace("SkyRelicM", "该记录不可复仇。\n");
        return false;
    end

    -- 是否已经无效了
    if not isValidRecord(target) then
        trace("SkyRelicM", "该记录已经失效。\n");
        return false;
    end

    -- 发起复仇
    Operation.cmd_do_revenge(target, force, stoneId);
    return true;
end

-- 计算是否处于冷却时间
function isInCoolTime(info, calcType)
    local cd = FormulaM.invoke("CALC_ROB_FAIL_WAIT_TIME", info["fail_times"] or 0, calcType == "boss");

    local lastTime;
    if calcType == "revenge" then
        lastTime = info["lastRevengeTime"] or data["last_revenge_time"] or 0;
    elseif calcType == "relic" then
        lastTime = info["last_rob_time"] or 0;
    elseif calcType == "boss" then
        lastTime = info["last_fail"] or 0;
    end

    if lastTime + cd > TimeM.getServerTime() then
        return true;
    end

    return false;
end

-- 判断记录是否有效
function isValidRecord(target)
    local record = ME.user.dbase:query("relic_combat_record", {})[target];
    if not record then
        -- 根本就没有此记录
        return false;
    end

    local defenseTime = record["strike_time"] or 0;
    local keepTime = defenseTime + RELIC_LONG_KEEP_TIME;

-- 所有记录都改为只保留72小时
--    -- 如果是防守成功了
--    if record["result"] ~= 1 then
--        keepTime = defenseTime + RELIC_SHORT_KEEP_TIME;
--    end
--
--    -- 如果是已经复仇过了
--    if record["revenge_time"] then
--        keepTime = record["revenge_time"] + RELIC_SHORT_KEEP_TIME;
--    end

    return keepTime >= TimeM.getServerTime();
end

-- 获取记录失效倒计时
function getVaildLeftTime(target)
    local record = ME.user.dbase:query("relic_combat_record", {})[target];
    if not record then
        -- 根本就没有此记录
        return 0;
    end
    local defenseTime = record["strike_time"] or 0;
    local keepTime = defenseTime + RELIC_LONG_KEEP_TIME;
    return keepTime - TimeM.getServerTime();
end

-- 获取所有有效记录
function getAllRecords()
    -- 按时间排序
    local ret = {};
    local record = ME.user.dbase:query("relic_combat_record", {});
    for rid, data in pairs(record) do
        if isValidRecord(rid) then
            -- 有效的记录
            table.insert(ret, getRevengeInfo(data));
        end
    end

    table.sort(ret, sortRecordByStrikeTime);
    return ret;
end

-- 排序(时间近的靠前)
function sortRecordByStrikeTime(info1, info2)
    return tonumber(info1["strike_time"]) > tonumber(info2["strike_time"]);
end

-- 排序2(时间早的靠前)
function sortRecordByStrikeTime2(info1, info2)
    return tonumber(info1["strike_time"]) < tonumber(info2["strike_time"]);
end

-- 获取当前有多少被掠夺并且成功的记录
function getRobRecord()
    -- 按规则排序
    local ret = {};
    local record = ME.user.dbase:query("relic_combat_record", {});
    for rid, data in pairs(record) do
        if isValidRecord(rid) and data["result"] == 1
            and not data["revenge_time"] then
            table.insert(ret, getRevengeInfo(data));
        end
    end
    table.sort(ret, sortRecordByStrikeTime2);
    return ret;
end

-- 播放录像
function playVideo(target)
    local record = ME.user.dbase:query("relic_combat_record", {})[target];
    if not record or not record["record"] then
        -- 没有录像
        return false;
    end

    -- local bonus = FormulaM.invoke("CALC_RELIC_ROB_BONUS",
    --     tonumber(record.real_rob or record.scrap) + tonumber(record.ex_resource), tonumber(record.money));

    local bonus = {};
    local scrap = tonumber(record.real_rob or record.scrap) + tonumber(record.ex_resource);
    if scrap then
        table.insert(bonus, { 1, 10000, scrap, });
    end

    local money = record.money or 0;
    if money > 0 then
        table.insert(bonus, { 2, "money", money, });
    end

    -- 添加字段表示是录像
    record.defenser["video"] = true;

    -- 创建一个战场
    SkyCombatM.createBattle(record.attacker, record.defenser, record.result, record.record, bonus);
end

-- 获取位置信息
function getRelicElementPos(posType, args)
    local relicElementPos = ME.user.dbase:query("relic_element_pos");
    for pos, posInfo in pairs(relicElementPos) do
        if posInfo[1] == posType and posInfo[2] == args then
            -- 返回位置
            return pos;
        end
    end
    return 0;
end

-- 删除位置信息
function deleteRelicPosInfo(pos)
    local relicElementPos = ME.user.dbase:query("relic_element_pos");
    if relicElementPos[pos] then
        relicElementPos[pos] = nil;
    end
    ME.user.dbase:set("relic_element_pos", relicElementPos);
end

-- 获取当前有多少掠夺有效点位
function getRelicElementNum()
    local relicElementPos = ME.user.dbase:query("relic_element_pos") or {};
    local num = 0;
    for _, posInfo in pairs(relicElementPos) do
        num = num + 1;
    end
    return num;
end

-- 删除位置信息
function deleteRelicPosInfoByType(posType, args)
    local relicElementPos = ME.user.dbase:query("relic_element_pos");
    for pos, posInfo in pairs(relicElementPos) do
        if posInfo[1] == posType and posInfo[2] == args then
            -- 删除
            deleteRelicPosInfo(pos);
            break;
        end
    end
end

-- 发起挑战遗迹BOSS
function strikeRelicBoss(force)
    local bossInfo = ME.user.dbase:query("relic_boss_info");
    if not bossInfo then
        trace("SkyRelicM", "玩家没有遗迹BOSS信息，不能发起挑战。\n");
        return false;
    end

    -- 是否已经死了
    if bossInfo["is_dead"] == 1 then
        trace("SkyRelicM", "玩家遗迹BOSS已经死亡，不能发起挑战。\n");
        return false;
    end

    -- 是否已经失效
    local curTime = TimeM.getServerTime();
    local lastAppearTime = bossInfo["last_appear_time"];
    if curTime > lastAppearTime + SECONDS_PER_DAY then
        trace("SkyRelicM", "玩家遗迹BOSS已经失效，不能发起挑战。\n");
        return false;
    end

    -- 是否处于冷却时间
    local lastTime = bossInfo["last_fail"] or 0;
    local cd = FormulaM.invoke("CALC_ROB_FAIL_WAIT_TIME", bossInfo["fail_times"] or 0, true);
    if lastTime + cd > TimeM.getServerTime() then
        if not force then
            trace("SkyRelicM", "玩家遗迹BOSS处于冷却时间，不能发起挑战。\n");
            return false;
        end
    end

    -- 发起挑战
    Operation.cmd_relic_strike_boss(force or 0);
end

-- 当前是否可以更新目标
function canChangeTarget(targetRid)
    local m = ME.user.dbase:query("relic_rob_list", {});
    if not m[targetRid] then
        trace("SkyRelicM", "玩家没有刷新目标：%s", tostring(targetRid));
        return false;
    end

    -- 判断是否已经赢过了
    if m[targetRid]["result"] == 1 then
        trace("SkyRelicM", "该遗迹目标已经掠夺成功，无法更换。\n");
        return false;
    end

    -- 判断是否刷新时间
    local curTime = TimeM.getServerTime();
    if m[targetRid]["can_change_time"] ~= nil and curTime <= m[targetRid]["can_change_time"] then
        -- trace("SkyRelicM", "还未到刷新时间，无法更换。"..debug.traceback());
        return false;
    end

    return true;
end

-- 刷新遗迹目标
function changeRelicTarget(targetRid)
    if not canChangeTarget(targetRid) then
        return;
    end
    -- 更换目标
    Operation.cmd_relic_change_target(targetRid);
end

-- 领取遗迹彩蛋
function takeRelicEggsBonus(eggId)
    local eggInfo = ME.user.dbase:query("relic_eggs_info", {});
    local eggList = eggInfo["egg_list"];

    if not eggInfo or not eggList then
        trace("SkyRelicM", "玩家当前没有彩蛋信息。\n");
        return false;
    end

    if table.indexOf(eggList, eggId) == -1 then
        trace("SkyRelicM", "玩家没有该彩蛋：%d。\n", eggId);
        return false;
    end

    -- 领取
    Operation.cmd_relic_take_eggs_bonus(eggId);
end

-- 获取刷新间隔时间
function getRobRefreshTime()
    local prop = PropM.combine(ME.user, "reduce_sky_time", "rob_refresh");
    local refresh_relic_time = PropM.apply(prop, REFRESH_RELIC_TIME);

    return refresh_relic_time;
end

-- 获取玩家剩余刷新时间
function getRelicRefreshLeftTime()
    local nextRefreshTime = ME.user.dbase:query("next_relic_time", 0);
    local curTime = TimeM.getServerTime();
    local leftTime = nextRefreshTime - curTime;
    if leftTime > getRobRefreshTime() or leftTime <= 0 then
        -- 超过8小时，以及小于8小时都认为是没有倒计时
        return 0;
    end

    return leftTime;
end

-- 查询BOSS信息
function queryBoss(id, path)
    local m = relicBoss[id]

    if not m or path == "" or not path then
        return m
    end

    return m[path]
end

-- 查询彩蛋信息
function queryEggs(id, path)
    local m = relicEggs[id]

    if not m or path == "" or not path then
        return m
    end

    return m[path]
end

-- 获取BOSS的详细信息
function getBossDetailInfo()
    local ret = initBossData();
    ret["type"] = "boss";
    return ret;
end

-- 初始化BOSS属性(UI显示用，只有部分数据)
function initBossData()
    local relicBossInfo = ME.user.dbase:query("relic_boss_info", {});
    local leftHp = relicBossInfo["left_hp"];
    local combatRecord = relicBossInfo["combat_record"] or 0;
    local winTimes = relicBossInfo["win_times"] or 0;
    local id = relicBossInfo["boss_id"];

    -- 如果不是第一次初始化
    local ret = {};
    local initBossInfo = relicBossInfo["init_boss_data"];
    if type(initBossInfo) == "table" then
        -- 直接使用旧的数据
        ret = initBossInfo;

        -- 名称需要重新获取一下，防止切换语言后显示不出来
        ret["name"] = queryBoss(id, "name");
    else
        local data = queryBoss(id);
        local combatDbase = data["combat_dbase"];
        local army = data["army"] or {};

        -- 玩家历史最高战力
        local playerAbility = ME.user.dbase:query("max_fight_ability", 0);
        -- BOSS战力
        local ability = FormulaM.invoke("CALC_RELIC_BOSS_FIGHT_ABILITY", id, combatRecord, playerAbility);
        -- 战力等级
        local level = FormulaM.invoke("CALC_LEVEL_BY_ABILITY", ability);

        ret = {
            ["fight_ability"] = ability,
            ["name"]  = data["name"],
            ["fire"]  = FormulaM.invoke("CALC_RELIC_BOSS_INIT_ATTRIB", id, level, combatDbase["fire"]),
            ["armor"] = FormulaM.invoke("CALC_RELIC_BOSS_INIT_ATTRIB", id, level, combatDbase["armor"]),
            ["speed"] = FormulaM.invoke("CALC_RELIC_BOSS_INIT_ATTRIB", id, level, combatDbase["speed"]),
            ["luck"]  = FormulaM.invoke("CALC_RELIC_BOSS_INIT_ATTRIB", id, level, combatDbase["luck"]),
            ["pets"]  = army,
        }
    end

    -- 初始血量
    if type(leftHp) == "number" and leftHp > 0 then
        ret["left_hp"] = leftHp;
    else
        ret["left_hp"] = ret["fight_ability"];
    end

    -- 奖励
    ret["bonus"] = relicBossInfo["bonus"];

    -- 失败次数
    ret["fail_times"] = relicBossInfo["fail_times"];
    ret["last_fail"] = relicBossInfo["last_fail"];

    -- 上次出现时间
    ret["last_appear_time"] = relicBossInfo["last_appear_time"];

    return ret;
end

-- 刷新掠夺目标信息
function refreshRelicInfos()
    -- 请求刷新
    Operation.cmd_refresh_relic_target_info();

    -- 记录刷新时间
    lastRefreshInfoTime = TimeM.getServerTime();
end

-- 玩家登陆回调
function whenLoginOk()
    -- 记录刷新时间
    lastRefreshInfoTime = TimeM.getServerTime();
end

-- 获取显示的宠物ID
function getShowPetId(showType, rid)
    if showType == "relic" then
        return relicPetShowList[rid];
    else
        return revengePetShowList[rid];
    end
end

-- 设置显示的宠物ID
function setShowPetId(showType, rid, petId)
    if showType == "relic" then
        relicPetShowList[rid] = petId;
    else
        revengePetShowList[rid] = petId;
    end
end

-- 遗迹BOSS出现时间
function getBossAppearTime()
    local relicBossInfo = ME.user.dbase:query("relic_boss_info", {});
    return relicBossInfo["last_appear_time"] or 0;
end

-- 矿车装满时间
function getCollectCompleteTime()
    local relicInfo = getMyRelicInfo("resource");

    -- 查询不到
    if not relicInfo then
        return 0;
    end

    local nowCount;
    if relicInfo["resource"] then
        nowCount = 0;
    else
        nowCount = relicInfo["scrap"] or 0
    end

    local storeSize = ME.user:queryAttrib("relic_store") or 0;
    local difNum = storeSize - nowCount;
    if difNum <= 0 then
        return 0;
    end

    local speed = ME.user:queryAttrib("scrap_speed") * 3600 / getGatherUnitTime();
    local hour = difNum / speed;

    return hour * 3600;
end
